package queue

import (
	"bytes"
	"errors"
	"fmt"
	"runtime"
	"strconv"
	"sync"
	"time"
)

type msg struct {
	data      string
	timestamp time.Time
}

type Broadcasting struct {
	timeout   int64 //数据过期时间
	mu        sync.Mutex
	msg       []msg
	Receivers map[string]*Receiver
}

func New(timeout int64) *Broadcasting {
	bs := &Broadcasting{timeout: timeout, Receivers: make(map[string]*Receiver)}
	bs.run()
	return bs
}

func (bs *Broadcasting) run() {
	//清理过期数据
	go func() {
		if bs.timeout > 0 {
			for {
				<-time.After(time.Duration(bs.timeout) * time.Second)
				now := time.Now().Unix()
				index := 0
				for i, v := range bs.msg {
					if now-v.timestamp.Unix() < bs.timeout {
						index = i
						break
					}
				}
				fmt.Println("clearInvalidData:", index)
				clearInvalidData(bs, index)
			}
		}
	}()
}

func clearInvalidData(bs *Broadcasting, i int) {
	//bs.mu.Lock()
	//bs.msg = bs.msg[i:]
	//bs.mu.Unlock()
	//for _, vv := range bs.Receivers {
	//	atomic.AddInt32(&vv.index, int32(-i))
	//}
}

func (bs *Broadcasting) Broadcast(data string) {
	bs.mu.Lock()
	defer bs.mu.Unlock()
	m := msg{
		data:      data,
		timestamp: time.Now(),
	}
	bs.msg = append(bs.msg, m)
}

func (bs *Broadcasting) Register(name string, maxConcurrency uint, fun func(string)) error {
	bs.mu.Lock()
	defer bs.mu.Unlock()
	if _, ok := bs.Receivers[name]; ok {
		return errors.New("this registrant of " + name + " is already exists")
	}
	if maxConcurrency <= 0 {
		maxConcurrency = 1
	}
	index := len(bs.msg) - 1
	if index < 0 {
		index = 0
	}
	rc := &Receiver{index: int32(index), handler: fun, maxConcurrency: int32(maxConcurrency), in: make(chan *Invoke), bs: bs}
	go rc.run()
	bs.Receivers[name] = rc
	return nil
}

func (bs *Broadcasting) UnRegister(name string) bool {
	bs.mu.Lock()
	defer bs.mu.Unlock()
	if _, ok := bs.Receivers[name]; ok {
		delete(bs.Receivers, name)
	}
	return true
}

func getGID() uint64 {
	b := make([]byte, 64)
	b = b[:runtime.Stack(b, false)]
	b = bytes.TrimPrefix(b, []byte("goroutine "))
	b = b[:bytes.IndexByte(b, ' ')]
	n, _ := strconv.ParseUint(string(b), 10, 64)
	return n
}
